home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.0-b / stk-3 / blt-for-STk-3.0 / blt-1.9 / src / bltGraph.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-12-20  |  41.5 KB  |  1,333 lines

  1. /*
  2.  * bltGraph.c --
  3.  *
  4.  *    This module implements a graph widget for the
  5.  *    Tk toolkit.
  6.  *
  7.  * Copyright 1991-1994 by AT&T Bell Laboratories.
  8.  * Permission to use, copy, modify, and distribute this software
  9.  * and its documentation for any purpose and without fee is hereby
  10.  * granted, provided that the above copyright notice appear in all
  11.  * copies and that both that the copyright notice and warranty
  12.  * disclaimer appear in supporting documentation, and that the
  13.  * names of AT&T Bell Laboratories any of their entities not be used
  14.  * in advertising or publicity pertaining to distribution of the
  15.  * software without specific, written prior permission.
  16.  *
  17.  * AT&T disclaims all warranties with regard to this software, including
  18.  * all implied warranties of merchantability and fitness.  In no event
  19.  * shall AT&T be liable for any special, indirect or consequential
  20.  * damages or any damages whatsoever resulting from loss of use, data
  21.  * or profits, whether in an action of contract, negligence or other
  22.  * tortuous action, arising out of or in connection with the use or
  23.  * performance of this software.
  24.  *
  25.  * Graph widget created by Sani Nassif and George Howlett.
  26.  */
  27.  
  28. /*
  29.  * To do:
  30.  *
  31.  * 1) Fix log manual scale for log axes.
  32.  *
  33.  * 2) Update manual pages.
  34.  *
  35.  * 3) Update comments.
  36.  *
  37.  * 5) Contour and flow graphs
  38.  *
  39.  * 6) Account for roundoff error when calculating bar widths
  40.  *
  41.  * 7) Arrows for line tags
  42.  *
  43.  * 9) Make sure reasonable defaults show for configuration items
  44.  *
  45.  * 10) Rubberbox for zooming???
  46.  */
  47.  
  48. #include "blt.h"
  49. #include "bltGraph.h"
  50. #include "bltGrTag.h"
  51. #include "bltGrElem.h"
  52. #include <X11/Xutil.h>
  53. #include <X11/Xatom.h>
  54.  
  55. #ifndef GRAPH_VERSION
  56. #define GRAPH_VERSION "4.1"
  57. #endif
  58.  
  59. static char *classNames[] =
  60. {
  61.     "Blt_graph", "Blt_barchart"
  62. };
  63.  
  64. static unsigned int configFlags[] =
  65. {
  66.     XYGRAPH_MASK, BARCHART_MASK
  67. };
  68.  
  69. #define DEF_GRAPH_BAR_WIDTH    "0.95"
  70. #define DEF_GRAPH_BG_COLOR    BG2
  71. #define DEF_GRAPH_BG_MONO    WHITE
  72. #define DEF_GRAPH_BORDER_WIDTH    "0"
  73. #define DEF_GRAPH_BORDER_WIDTH  "0"
  74. #define DEF_GRAPH_BUFFERED    "1"
  75. #define DEF_GRAPH_CURSOR      "crosshair"
  76. #define DEF_GRAPH_FG_COLOR    BLACK
  77. #define DEF_GRAPH_FG_MONO    BLACK
  78. #define DEF_GRAPH_FONT        "-Adobe-Helvetica-Bold-R-Normal--*-120-*-*-*-*-*-*"
  79. #define DEF_GRAPH_HALO        "0.5i"
  80. #define DEF_GRAPH_HALO_BAR    "0.1i"
  81. #define DEF_GRAPH_HEIGHT    "400"
  82. #define DEF_GRAPH_INVERT_XY    "0"
  83. #define DEF_GRAPH_MARGIN    "0"
  84. #define DEF_GRAPH_PLOT_BG_COLOR    ANTIQUEWHITE1
  85. #define DEF_GRAPH_PLOT_BG_MONO    WHITE
  86. #define DEF_GRAPH_PLOT_BW_COLOR "2"
  87. #define DEF_GRAPH_PLOT_BW_MONO  "0"
  88. #define DEF_GRAPH_PLOT_RELIEF    "sunken"
  89. #define DEF_GRAPH_RELIEF    "flat"
  90. #define DEF_GRAPH_TITLE        (char *)NULL
  91. #define DEF_GRAPH_WIDTH        "400"
  92.  
  93. static Tk_ConfigSpec configSpecs[] =
  94. {
  95.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  96.     DEF_GRAPH_BG_COLOR, Tk_Offset(Graph, border),
  97.     TK_CONFIG_COLOR_ONLY | ALL_MASK},
  98.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  99.     DEF_GRAPH_BG_MONO, Tk_Offset(Graph, border),
  100.     TK_CONFIG_MONO_ONLY | ALL_MASK},
  101.     {TK_CONFIG_DOUBLE, "-barwidth", "barWidth", "BarWidth",
  102.     DEF_GRAPH_BAR_WIDTH, Tk_Offset(Graph, barWidth), BARCHART_MASK},
  103.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0,
  104.     ALL_MASK},
  105.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0,
  106.     ALL_MASK},
  107.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  108.     DEF_GRAPH_BORDER_WIDTH, Tk_Offset(Graph, borderWidth),
  109.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  110.     {TK_CONFIG_PIXELS, "-bottommargin", "bottomMargin", "Margin",
  111.     DEF_GRAPH_MARGIN, Tk_Offset(Graph, bottomMargin), ALL_MASK},
  112.     {TK_CONFIG_BOOLEAN, "-bufferelements", "bufferElements", "BufferElements",
  113.     DEF_GRAPH_BUFFERED, Tk_Offset(Graph, buffered),
  114.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  115.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  116.     DEF_GRAPH_CURSOR, Tk_Offset(Graph, cursor),
  117.     ALL_MASK | TK_CONFIG_NULL_OK},
  118.     {TK_CONFIG_FONT, "-font", "font", "Font",
  119.     DEF_GRAPH_FONT, Tk_Offset(Graph, fontPtr), ALL_MASK},
  120.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0,
  121.     ALL_MASK},
  122.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  123.     DEF_GRAPH_FG_COLOR, Tk_Offset(Graph, marginFg),
  124.     TK_CONFIG_COLOR_ONLY | ALL_MASK},
  125.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  126.     DEF_GRAPH_FG_MONO, Tk_Offset(Graph, marginFg),
  127.     TK_CONFIG_MONO_ONLY | ALL_MASK},
  128.     {TK_CONFIG_PIXELS, "-halo", "halo", "Halo",
  129.     DEF_GRAPH_HALO_BAR, Tk_Offset(Graph, halo), BARCHART_MASK},
  130.     {TK_CONFIG_PIXELS, "-halo", "halo", "Halo",
  131.     DEF_GRAPH_HALO, Tk_Offset(Graph, halo), XYGRAPH_MASK},
  132.     {TK_CONFIG_PIXELS, "-height", "height", "Height",
  133.     DEF_GRAPH_HEIGHT, Tk_Offset(Graph, reqHeight),
  134.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  135.     {TK_CONFIG_BOOLEAN, "-invertxy", "invertXY", "InvertXY",
  136.     DEF_GRAPH_INVERT_XY, Tk_Offset(Graph, inverted),
  137.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  138.     {TK_CONFIG_PIXELS, "-leftmargin", "leftMargin", "Margin",
  139.     DEF_GRAPH_MARGIN, Tk_Offset(Graph, leftMargin),
  140.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  141.     {TK_CONFIG_COLOR, "-plotbackground", "plotBackground", "Background",
  142.     DEF_GRAPH_PLOT_BG_MONO, Tk_Offset(Graph, plotBg),
  143.     TK_CONFIG_MONO_ONLY | ALL_MASK},
  144.     {TK_CONFIG_COLOR, "-plotbackground", "plotBackground", "Background",
  145.     DEF_GRAPH_PLOT_BG_COLOR, Tk_Offset(Graph, plotBg),
  146.     TK_CONFIG_COLOR_ONLY | ALL_MASK},
  147.     {TK_CONFIG_INT, "-plotborderwidth", "plotBorderWidth", "BorderWidth",
  148.     DEF_GRAPH_PLOT_BW_COLOR, Tk_Offset(Graph, plotBW),
  149.     ALL_MASK | TK_CONFIG_COLOR_ONLY},
  150.     {TK_CONFIG_INT, "-plotborderwidth", "plotBorderWidth", "BorderWidth",
  151.     DEF_GRAPH_PLOT_BW_MONO, Tk_Offset(Graph, plotBW),
  152.     ALL_MASK | TK_CONFIG_MONO_ONLY},
  153.     {TK_CONFIG_RELIEF, "-plotrelief", "plotRelief", "Relief",
  154.     DEF_GRAPH_PLOT_RELIEF, Tk_Offset(Graph, plotRelief),
  155.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  156.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  157.     DEF_GRAPH_RELIEF, Tk_Offset(Graph, relief),
  158.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  159.     {TK_CONFIG_PIXELS, "-rightmargin", "rightMargin", "Margin",
  160.     DEF_GRAPH_MARGIN, Tk_Offset(Graph, rightMargin),
  161.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  162.     {TK_CONFIG_STRING, "-title", "title", "Title",
  163.     DEF_GRAPH_TITLE, Tk_Offset(Graph, title),
  164.     ALL_MASK | TK_CONFIG_NULL_OK},
  165.     {TK_CONFIG_PIXELS, "-topmargin", "topMargin", "Margin",
  166.     DEF_GRAPH_MARGIN, Tk_Offset(Graph, topMargin),
  167.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  168.     {TK_CONFIG_PIXELS, "-width", "width", "Width",
  169.     DEF_GRAPH_WIDTH, Tk_Offset(Graph, reqWidth),
  170.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  171.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  172. };
  173.  
  174. double Blt_negInfinity, Blt_posInfinity;
  175.  
  176. extern int Blt_CreateAxis _ANSI_ARGS_((Graph *graphPtr, AxisType type, int));
  177. extern int Blt_AxisCmd _ANSI_ARGS_((Graph *graphPtr, GraphAxis *axisPtr,
  178.     int argc, char **argv, int flags));
  179. extern void Blt_ComputeAxes _ANSI_ARGS_((Graph *graphPtr));
  180. extern void Blt_UpdateAxisBackgrounds _ANSI_ARGS_((Graph *graphPtr,
  181.     XColor *bgColorPtr));
  182. extern int Blt_ComputeLayout _ANSI_ARGS_((Graph *graphPtr));
  183. extern int Blt_LegendCmd _ANSI_ARGS_((Graph *graphPtr, int argc, char **argv));
  184. extern int Blt_CreateLegend _ANSI_ARGS_((Graph *graphPtr));
  185. extern int Blt_CreatePostScript _ANSI_ARGS_((Graph *graphPtr));
  186. extern int Blt_CreateCrosshairs _ANSI_ARGS_((Graph *graphPtr));
  187. extern int Blt_CrosshairsCmd _ANSI_ARGS_((Graph *graphPtr, int argc,
  188.     char **argv));
  189. static void DisplayGraph _ANSI_ARGS_((ClientData clientData));
  190. static void DestroyGraph _ANSI_ARGS_((ClientData clientData));
  191. static int GraphWidgetCmd _ANSI_ARGS_((ClientData clientData,
  192.     Tcl_Interp *interp, int argc, char **argv));
  193. extern int Blt_TagCmd _ANSI_ARGS_((Graph *graphPtr, int argc, char **argv));
  194. extern int Blt_ElementCmd _ANSI_ARGS_((Graph *graphPtr, int argc,
  195.     char **argv));
  196.  
  197. /*
  198.  *--------------------------------------------------------------
  199.  *
  200.  * Blt_RedrawGraph --
  201.  *
  202.  *    Tell the Tk dispatcher to call the graph display routine at
  203.  *    the next idle point.  This request is made only if the window
  204.  *    is mapped and no other redraw request is pending.
  205.  *
  206.  * Results:
  207.  *    None.
  208.  *
  209.  * Side effects:
  210.  *    The window is eventually redisplayed.
  211.  *
  212.  *--------------------------------------------------------------
  213.  */
  214. void
  215. Blt_RedrawGraph(graphPtr)
  216.     Graph *graphPtr;        /* Graph widget record */
  217. {
  218.     if (Tk_IsMapped(graphPtr->tkwin) && !(graphPtr->flags & REDRAW_PENDING)) {
  219.     Tk_DoWhenIdle(DisplayGraph, (ClientData)graphPtr);
  220.     graphPtr->flags |= REDRAW_PENDING;
  221.     }
  222. }
  223.  
  224. /*
  225.  *--------------------------------------------------------------
  226.  *
  227.  * GraphEventProc --
  228.  *
  229.  *    This procedure is invoked by the Tk dispatcher for various
  230.  *    events on graphs.
  231.  *
  232.  * Results:
  233.  *    None.
  234.  *
  235.  * Side effects:
  236.  *    When the window gets deleted, internal structures get cleaned
  237.  *    up.  When it gets exposed, the graph is eventually redisplayed.
  238.  *
  239.  *--------------------------------------------------------------
  240.  */
  241. static void
  242. GraphEventProc(clientData, eventPtr)
  243.     ClientData clientData;    /* Graph widget record */
  244.     register XEvent *eventPtr;    /* Event which triggered call to routine */
  245. {
  246.     register Graph *graphPtr = (Graph *)clientData;
  247.  
  248.     if (eventPtr->type == Expose) {
  249.     if (eventPtr->xexpose.count == 0) {
  250.         graphPtr->flags |= REFRESH;
  251.         Blt_RedrawGraph(graphPtr);
  252.     }
  253.     } else if (eventPtr->type == DestroyNotify) {
  254.     Tcl_DeleteCommand(graphPtr->interp, Tk_PathName(graphPtr->tkwin));
  255.     graphPtr->tkwin = NULL;
  256.     if (graphPtr->flags & REDRAW_PENDING) {
  257.         Tk_CancelIdleCall(DisplayGraph, (ClientData)graphPtr);
  258.     }
  259. #if (TK_MINOR_VERSION > 0)
  260.     Tk_EventuallyFree((ClientData)graphPtr, (Tcl_FreeProc *)DestroyGraph);
  261. #else
  262.     Tk_EventuallyFree((ClientData)graphPtr, (Tk_FreeProc *)DestroyGraph);
  263. #endif
  264.     } else if (eventPtr->type == ConfigureNotify) {
  265.     graphPtr->flags |= (LAYOUT_ALL | REFRESH);
  266.     Blt_RedrawGraph(graphPtr);
  267.     }
  268. }
  269.  
  270. /*
  271.  *--------------------------------------------------------------
  272.  *
  273.  * GraphCoords --
  274.  *
  275.  *    This procedure returns a list of the graph coordinate
  276.  *    values corresponding with the given window X and Y
  277.  *    coordinate positions.
  278.  *
  279.  * Results:
  280.  *    Returns a standard Tcl result.  The interp->result field is
  281.  *    a Tcl list of the corresponding graph X and Y coordinates.
  282.  *    If an error occurred while parsing the window positions,
  283.  *    TCL_ERROR is returned, and interp->result will contain
  284.  *    the error message.
  285.  *
  286.  *--------------------------------------------------------------
  287.  */
  288. static int
  289. GraphCoords(graphPtr, argc, argv)
  290.     Graph *graphPtr;        /* Graph widget record */
  291.     int argc;
  292.     char **argv;
  293. {
  294.     int winX, winY;        /* Integer window coordinates representation */
  295.     char string[TCL_DOUBLE_SPACE + 1];
  296.     double x, y;
  297.  
  298.     if (argc != 4) {
  299.     Tcl_AppendResult(graphPtr->interp, "wrong # args: should be \"",
  300.         argv[0], " invtransform winX winY\"", NULL);
  301.     return TCL_ERROR;
  302.     }
  303.     if (Tcl_GetInt(graphPtr->interp, argv[2], &winX) != TCL_OK ||
  304.     Tcl_GetInt(graphPtr->interp, argv[3], &winY) != TCL_OK) {
  305.     return TCL_ERROR;
  306.     }
  307.     /*
  308.      * Perform the reverse transformation from window coordinates to
  309.      * data coordinates
  310.      */
  311.     x = Blt_InvTransform(graphPtr->axisArr[X1_AXIS], winX);
  312.     y = Blt_InvTransform(graphPtr->axisArr[Y1_AXIS], winY);
  313.  
  314.     if (graphPtr->inverted) {
  315.     Tcl_PrintDouble(graphPtr->interp, y, string);
  316.     Tcl_AppendElement(graphPtr->interp, string);
  317.     Tcl_PrintDouble(graphPtr->interp, x, string);
  318.     Tcl_AppendElement(graphPtr->interp, string);
  319.     } else {
  320.     Tcl_PrintDouble(graphPtr->interp, x, string);
  321.     Tcl_AppendElement(graphPtr->interp, string);
  322.     Tcl_PrintDouble(graphPtr->interp, y, string);
  323.     Tcl_AppendElement(graphPtr->interp, string);
  324.     }
  325.     return TCL_OK;
  326. }
  327.  
  328. /*
  329.  *--------------------------------------------------------------
  330.  *
  331.  * WindowCoords --
  332.  *
  333.  *    This procedure returns a list of the window coordinates
  334.  *    corresponding with the given graph x and y coordinates.
  335.  *
  336.  * Results:
  337.  *    Returns a standard Tcl result.  interp->result contains
  338.  *    the list of the graph coordinates. If an error occurred
  339.  *    while parsing the window positions, TCL_ERROR is returned,
  340.  *    then interp->result will contain an error message.
  341.  *
  342.  *--------------------------------------------------------------
  343.  */
  344. static int
  345. WindowCoords(graphPtr, argc, argv)
  346.     Graph *graphPtr;        /* Graph widget record */
  347.     int argc;
  348.     char **argv;
  349. {
  350.     double x, y;
  351.     char string[TCL_DOUBLE_SPACE + 1];
  352.  
  353.     if (argc != 4) {
  354.     Tcl_AppendResult(graphPtr->interp, "wrong # args: should be \"",
  355.         argv[0], " transform x y\"", NULL);
  356.     return TCL_ERROR;
  357.     }
  358.     if (Tcl_ExprDouble(graphPtr->interp, argv[2], &x) != TCL_OK ||
  359.     Tcl_ExprDouble(graphPtr->interp, argv[3], &y) != TCL_OK) {
  360.     return TCL_ERROR;
  361.     }
  362.     /* Perform the transformation from window coordinates to data */
  363.     if (graphPtr->inverted) {
  364.     sprintf(string, "%d", Blt_Transform(graphPtr->axisArr[Y1_AXIS], y));
  365.     Tcl_AppendElement(graphPtr->interp, string);
  366.     sprintf(string, "%d", Blt_Transform(graphPtr->axisArr[X1_AXIS], x));
  367.     Tcl_AppendElement(graphPtr->interp, string);
  368.     } else {
  369.     sprintf(string, "%d", Blt_Transform(graphPtr->axisArr[X1_AXIS], x));
  370.     Tcl_AppendElement(graphPtr->interp, string);
  371.     sprintf(string, "%d", Blt_Transform(graphPtr->axisArr[Y1_AXIS], y));
  372.     Tcl_AppendElement(graphPtr->interp, string);
  373.     }
  374.     return TCL_OK;
  375. }
  376.  
  377. /*
  378.  *--------------------------------------------------------------
  379.  *
  380.  * AdjustAxisPointers --
  381.  *
  382.  *    Sets the axis pointers according to whether the axis is
  383.  *    inverted on not.  The axis locations are also reset.
  384.  *
  385.  * Results:
  386.  *    None.
  387.  *
  388.  *--------------------------------------------------------------
  389.  */
  390. static void
  391. AdjustAxisPointers(graphPtr)
  392.     Graph *graphPtr;        /* Graph widget record */
  393. {
  394.     if (graphPtr->inverted) {
  395.     graphPtr->bottomAxis = graphPtr->axisArr[Y1_AXIS];
  396.     graphPtr->leftAxis = graphPtr->axisArr[X1_AXIS];
  397.     graphPtr->topAxis = graphPtr->axisArr[Y2_AXIS];
  398.     graphPtr->rightAxis = graphPtr->axisArr[X2_AXIS];
  399.     } else {
  400.     graphPtr->bottomAxis = graphPtr->axisArr[X1_AXIS];
  401.     graphPtr->leftAxis = graphPtr->axisArr[Y1_AXIS];
  402.     graphPtr->topAxis = graphPtr->axisArr[X2_AXIS];
  403.     graphPtr->rightAxis = graphPtr->axisArr[Y2_AXIS];
  404.     }
  405.     graphPtr->bottomAxis->location = BOTTOM_AXIS;
  406.     graphPtr->leftAxis->location = LEFT_AXIS;
  407.     graphPtr->topAxis->location = TOP_AXIS;
  408.     graphPtr->rightAxis->location = RIGHT_AXIS;
  409. }
  410.  
  411. /*
  412.  *----------------------------------------------------------------------
  413.  *
  414.  * DestroyGraph --
  415.  *
  416.  *    This procedure is invoked by Tk_EventuallyFree or Tk_Release
  417.  *    to clean up the internal structure of a graph at a safe time
  418.  *    (when no-one is using it anymore).
  419.  *
  420.  * Results:
  421.  *    None.
  422.  *
  423.  * Side effects:
  424.  *    Everything associated with the widget is freed up.
  425.  *
  426.  *----------------------------------------------------------------------
  427.  */
  428. static void
  429. DestroyGraph(clientData)
  430.     ClientData clientData;
  431. {
  432.     register Graph *graphPtr = (Graph *)clientData;
  433.     Tcl_HashEntry *entryPtr;
  434.     Tcl_HashSearch cursor;
  435.     Element *elemPtr;
  436.     Tag *tagPtr;
  437.     register int i;
  438.     GraphAxis *axisPtr;
  439.  
  440.     /*
  441.      * Destroy the individual components of the graph: elements, tags,
  442.      * X and Y axes, legend, display lists etc.
  443.      */
  444.     for (entryPtr = Tcl_FirstHashEntry(&(graphPtr->elemTable), &cursor);
  445.     entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&cursor)) {
  446.     elemPtr = (Element *)Tcl_GetHashValue(entryPtr);
  447.     (*elemPtr->destroyProc) (graphPtr, elemPtr);
  448.     }
  449.     Tcl_DeleteHashTable(&(graphPtr->elemTable));
  450.     Blt_ClearList(&(graphPtr->elemList));
  451.     for (entryPtr = Tcl_FirstHashEntry(&(graphPtr->tagTable), &cursor);
  452.     entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&cursor)) {
  453.     tagPtr = (Tag *)Tcl_GetHashValue(entryPtr);
  454.     (*tagPtr->destroyProc) (graphPtr, tagPtr);
  455.     }
  456.     Tcl_DeleteHashTable(&(graphPtr->tagTable));
  457.     Blt_ClearList(&(graphPtr->tagList));
  458.  
  459.     for (i = 0; i < 4; i++) {
  460.     axisPtr = graphPtr->axisArr[i];
  461.     (*axisPtr->destroyProc) (graphPtr, axisPtr);
  462.     }
  463.     (*graphPtr->legendPtr->destroyProc) (graphPtr);
  464.     (*graphPtr->postscript->destroyProc) (graphPtr);
  465.     (*graphPtr->crosshairs->destroyProc) (graphPtr);
  466.  
  467.     /* Release allocated X resources and memory. */
  468.     if (graphPtr->marginGC != NULL) {
  469.     Tk_FreeGC(graphPtr->display, graphPtr->marginGC);
  470.     }
  471.     if (graphPtr->marginFillGC != NULL) {
  472.     Tk_FreeGC(graphPtr->display, graphPtr->marginFillGC);
  473.     }
  474.     if (graphPtr->elemMap != None) {
  475.     XFreePixmap(graphPtr->display, graphPtr->elemMap);
  476.     }
  477.     Tk_FreeOptions(configSpecs, (char *)graphPtr, graphPtr->display,
  478.     configFlags[graphPtr->type]);
  479.     free((char *)graphPtr);
  480. }
  481.  
  482. /*
  483.  *----------------------------------------------------------------------
  484.  *
  485.  * CreateGraph --
  486.  *
  487.  *    This procedure creates and initializes a new widget.
  488.  *
  489.  * Results:
  490.  *    The return value is a pointer to a structure describing
  491.  *    the new widget.  If an error occurred, then the return
  492.  *    value is NULL and an error message is left in interp->result.
  493.  *
  494.  * Side effects:
  495.  *    Memory is allocated, a Tk_Window is created, etc.
  496.  *
  497.  *----------------------------------------------------------------------
  498.  */
  499.  
  500. static Graph *
  501. CreateGraph(interp, parent, pathName, type)
  502.     Tcl_Interp *interp;
  503.     Tk_Window parent;
  504.     char *pathName;
  505.     GraphClassType type;
  506. {
  507.     Graph *graphPtr;
  508.     Tk_Window tkwin;
  509.     unsigned int flags;
  510.     register int i;
  511.  
  512.     tkwin = Tk_CreateWindowFromPath(interp, parent, pathName, (char *)NULL);
  513.     if (tkwin == (Tk_Window)NULL) {
  514.     return (Graph *) NULL;
  515.     }
  516.     graphPtr = (Graph *)calloc(1, sizeof(Graph));
  517.     if (graphPtr == (Graph *)NULL) {
  518.     interp->result = "can't allocate graph structure";
  519.     return (Graph *) NULL;
  520.     }
  521.     Tk_SetClass(tkwin, classNames[type]);
  522.  
  523.     /* Initialize the data structure for the graph. */
  524.  
  525.     graphPtr->tkwin = tkwin;
  526.     graphPtr->pathName = Tk_PathName(tkwin);
  527.     graphPtr->display = Tk_Display(tkwin);
  528.     graphPtr->interp = interp;
  529.     graphPtr->type = type;
  530.     graphPtr->width = graphPtr->height = 400;
  531.     graphPtr->reqWidth = graphPtr->reqHeight = 400;
  532.     graphPtr->buffered = 1;
  533.     graphPtr->plotRelief = TK_RELIEF_SUNKEN;
  534.     graphPtr->relief = TK_RELIEF_FLAT;
  535.     graphPtr->flags |= (DIRTY | LAYOUT_ALL);
  536.  
  537.     Tcl_InitHashTable(&(graphPtr->elemTable), TCL_STRING_KEYS);
  538.     Blt_InitLinkedList(&(graphPtr->elemList), TCL_STRING_KEYS);
  539.     Tcl_InitHashTable(&(graphPtr->tagTable), TCL_STRING_KEYS);
  540.     Blt_InitLinkedList(&(graphPtr->tagList), TCL_STRING_KEYS);
  541.  
  542.     graphPtr->nextTagId = 1;
  543.     /*
  544.      * Axes and tags (text, bitmap) need private GCs, so create the
  545.      * window now.
  546.      */
  547.     Tk_MakeWindowExist(tkwin);
  548.  
  549.     flags = configFlags[type];
  550.     for (i = 0; i < 4; i++) {
  551.     if (Blt_CreateAxis(graphPtr, (AxisType)i, flags) != TCL_OK) {
  552.         goto error;
  553.     }
  554.     }
  555.     AdjustAxisPointers(graphPtr);
  556.  
  557.     if (Blt_CreatePostScript(graphPtr) != TCL_OK) {
  558.     goto error;
  559.     }
  560.     if (Blt_CreateCrosshairs(graphPtr) != TCL_OK) {
  561.     goto error;
  562.     }
  563.     if (Blt_CreateLegend(graphPtr) != TCL_OK) {
  564.     goto error;
  565.     }
  566.     /* Compute the initial axis ticks and labels */
  567.  
  568.     Blt_ComputeAxes(graphPtr);
  569.     Tk_CreateEventHandler(graphPtr->tkwin, ExposureMask | StructureNotifyMask,
  570.     GraphEventProc, (ClientData)graphPtr);
  571.     Tcl_CreateCommand(interp, Tk_PathName(graphPtr->tkwin), GraphWidgetCmd,
  572.     (ClientData)graphPtr, (Tcl_CmdDeleteProc *)NULL);
  573.     return (graphPtr);
  574.  
  575.   error:
  576.     if (tkwin != (Tk_Window)NULL) {
  577.     Tk_DestroyWindow(tkwin);
  578.     }
  579.     return (Graph *) NULL;
  580. }
  581.  
  582. /*
  583.  *----------------------------------------------------------------------
  584.  *
  585.  * ConfigureGraph --
  586.  *
  587.  *    This procedure is called to process an argv/argc list, plus
  588.  *    the Tk option database, in order to configure (or
  589.  *    reconfigure) a graph widget.
  590.  *
  591.  * Results:
  592.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  593.  *    returned, then interp->result contains an error message.
  594.  *
  595.  * Side effects:
  596.  *    Configuration information, such as text string, colors, font,
  597.  *    etc. get set for graphPtr;  old resources get freed, if there
  598.  *    were any.  The graph is redisplayed.
  599.  *
  600.  *----------------------------------------------------------------------
  601.  */
  602. static int
  603. ConfigureGraph(graphPtr, argc, argv, flags)
  604.     register Graph *graphPtr;    /* Graph widget record */
  605.     int argc;            /* Number of configuration arguments */
  606.     char **argv;        /* Configuration arguments */
  607.     unsigned int flags;        /* Configuration flags */
  608. {
  609.     XColor *colorPtr;
  610.     GC newGC;
  611.     XGCValues gcValues;
  612.     unsigned long gcMask;
  613.  
  614.     if (Tk_ConfigureWidget(graphPtr->interp, graphPtr->tkwin, configSpecs,
  615.         argc, argv, (char *)graphPtr, flags) != TCL_OK) {
  616.     return TCL_ERROR;
  617.     }
  618.     if ((graphPtr->reqWidth < 1) || (graphPtr->reqHeight < 1)) {
  619.     Tcl_AppendResult(graphPtr->interp,
  620.         "impossible width/height specifications for \"",
  621.         Tk_PathName(graphPtr->tkwin), "\"", (char *)NULL);
  622.     return TCL_ERROR;
  623.     }
  624.     Tk_GeometryRequest(graphPtr->tkwin, graphPtr->reqWidth,
  625.     graphPtr->reqHeight);
  626.     Tk_SetInternalBorder(graphPtr->tkwin, graphPtr->borderWidth);
  627.     Tk_SetBackgroundFromBorder(graphPtr->tkwin, graphPtr->border);
  628.  
  629.     colorPtr = Tk_3DBorderColor(graphPtr->border);
  630.  
  631.     /* Update background color for axis text GCs */
  632.     Blt_UpdateAxisBackgrounds(graphPtr, colorPtr);
  633.  
  634.     /*
  635.      * Create GCs for interior and exterior regions, and a background
  636.      * GC for clearing the margins with XFillRectangle
  637.      */
  638.     gcValues.foreground = graphPtr->marginFg->pixel;
  639.     gcValues.background = colorPtr->pixel;
  640.     gcValues.font = graphPtr->fontPtr->fid;
  641.     gcMask = (GCForeground | GCBackground | GCFont);
  642.     newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
  643.     if (graphPtr->marginGC != NULL) {
  644.     Tk_FreeGC(graphPtr->display, graphPtr->marginGC);
  645.     }
  646.     graphPtr->marginGC = newGC;
  647.     gcValues.foreground = colorPtr->pixel;
  648.     gcValues.background = graphPtr->marginFg->pixel;
  649.     newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
  650.     if (graphPtr->marginFillGC != NULL) {
  651.     Tk_FreeGC(graphPtr->display, graphPtr->marginFillGC);
  652.     }
  653.     graphPtr->marginFillGC = newGC;
  654.     gcValues.foreground = graphPtr->plotBg->pixel;
  655.     newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
  656.     if (graphPtr->plotFillGC != NULL) {
  657.     Tk_FreeGC(graphPtr->display, graphPtr->plotFillGC);
  658.     }
  659.     graphPtr->plotFillGC = newGC;
  660.     /*
  661.      * If the -inverted option changed, we need to readjust the pointers
  662.      * to the axes and recompute the their scales.
  663.      */
  664.     if (Blt_OptionChanged(configSpecs, "-invertxy", (char *)NULL)) {
  665.     AdjustAxisPointers(graphPtr);
  666.     Blt_ComputeAxes(graphPtr);
  667.     }
  668.     /*
  669.      * Free the pixmap if we're not buffering the display of elements
  670.      * anymore.
  671.      */
  672.     if ((!graphPtr->buffered) && (graphPtr->elemMap != None)) {
  673.     XFreePixmap(graphPtr->display, graphPtr->elemMap);
  674.     graphPtr->elemMap = None;
  675.     }
  676.     /*
  677.      * Reconfigure the crosshairs in case the plotting area's
  678.      * background color changed
  679.      */
  680.     (*graphPtr->crosshairs->configProc) (graphPtr);
  681.  
  682.     /*
  683.      *  Update the layout of the graph (and redraw the elements) if
  684.      *  any of the following graph options which affect the size of
  685.      *    the plotting area has changed.
  686.      *
  687.      *      -borderwidth, -plotborderwidth
  688.      *        -font, -title
  689.      *        -width, -height
  690.      *        -invertxy
  691.      *        -bottommargin, -leftmargin, -rightmargin, -topmargin
  692.      *
  693.      */
  694.     if (Blt_OptionChanged(configSpecs, "-invertxy", "-title", "-font",
  695.         "-*margin", "-*width", "-height", (char *)NULL)) {
  696.     graphPtr->flags |= (DIRTY | LAYOUT_ALL);
  697.     }
  698.     graphPtr->flags |= REFRESH;
  699.     Blt_RedrawGraph(graphPtr);
  700.     return TCL_OK;
  701. }
  702.  
  703. /*
  704.  *--------------------------------------------------------------
  705.  *
  706.  * GraphCmd --
  707.  *
  708.  *    Creates a new window and Tcl command representing an
  709.  *    instance of a graph widget.
  710.  *
  711.  * Results:
  712.  *    A standard Tcl result.
  713.  *
  714.  * Side effects:
  715.  *    See the user documentation.
  716.  *
  717.  *--------------------------------------------------------------
  718.  */
  719. static int
  720. GraphCmd(clientData, interp, argc, argv)
  721.     ClientData clientData;
  722.     Tcl_Interp *interp;
  723.     int argc;
  724.     char **argv;
  725. {
  726.     Tk_Window tkwin = (Tk_Window)clientData;
  727.     register Graph *graphPtr;
  728.     GraphClassType type;
  729.     unsigned int flags;
  730.  
  731.     if (argc < 2) {
  732.     Tcl_AppendResult(interp, "wrong # args:  should be \"", argv[0],
  733.         " pathName ?options?\"", (char *)NULL);
  734.     return TCL_ERROR;
  735.     }
  736.     if (strcmp(argv[0], "blt_graph") == 0) {
  737.     type = GRAPH;
  738.     } else if (strcmp(argv[0], "blt_barchart") == 0) {
  739.     type = BARCHART;
  740.     } else if (strcmp(argv[0], "blt_contour") == 0) {
  741.     type = CONTOUR;
  742.     } else {
  743.     Tcl_AppendResult(interp, "unknown graph-creation command \"", argv[0],
  744.         "\"", (char *)NULL);
  745.     return TCL_ERROR;
  746.     }
  747.     Blt_negInfinity = (double)MAX_NEG_VAL;
  748.     Blt_posInfinity = (double)MAX_POS_VAL;
  749.     graphPtr = CreateGraph(interp, tkwin, argv[1], type);
  750.     if (graphPtr == NULL) {
  751.     return TCL_ERROR;
  752.     }
  753.     flags = configFlags[type];
  754.     if (ConfigureGraph(graphPtr, argc - 2, argv + 2, flags) != TCL_OK) {
  755.     Tk_DestroyWindow(graphPtr->tkwin);
  756.     return TCL_ERROR;
  757.     }
  758.     interp->result = graphPtr->pathName;
  759.     return TCL_OK;
  760. }
  761.  
  762. /*
  763.  *--------------------------------------------------------------
  764.  *
  765.  * GraphWidgetCmd --
  766.  *
  767.  *    This procedure is invoked to process the Tcl command
  768.  *    that corresponds to a widget managed by this module.
  769.  *    See the user documentation for details on what it does.
  770.  *
  771.  * Results:
  772.  *    A standard Tcl result.
  773.  *
  774.  * Side effects:
  775.  *    See the user documentation.
  776.  *
  777.  *--------------------------------------------------------------
  778.  */
  779. static int
  780. GraphWidgetCmd(clientData, interp, argc, argv)
  781.     ClientData clientData;
  782.     Tcl_Interp *interp;
  783.     int argc;
  784.     char **argv;
  785. {
  786.     register Graph *graphPtr = (Graph *)clientData;
  787.     int result = TCL_ERROR;
  788.     Tk_Window tkwin = graphPtr->tkwin;
  789.     char c;
  790.     unsigned int length;
  791.     unsigned int flags;
  792.     GraphClassType type;
  793.  
  794.     if (argc < 2) {
  795.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  796.         " option ?arg arg ...?\"", (char *)NULL);
  797.     return TCL_ERROR;
  798.     }
  799.     Tk_Preserve((ClientData)graphPtr);
  800.  
  801.     c = argv[1][0];
  802.     length = strlen(argv[1]);
  803.     type = graphPtr->type;
  804.     flags = (configFlags[type] | TK_CONFIG_ARGV_ONLY);
  805.  
  806.     if ((c == 'c') && (length > 1) &&
  807.     (strncmp(argv[1], "configure", length) == 0)) {
  808.     if (argc == 2)
  809.         result = Tk_ConfigureInfo(interp, tkwin, configSpecs,
  810.         (char *)graphPtr, (char *)NULL, flags);
  811.     else if (argc == 3)
  812.         result = Tk_ConfigureInfo(interp, tkwin, configSpecs,
  813.         (char *)graphPtr, argv[2], flags);
  814.     else {
  815.         result = ConfigureGraph(graphPtr, argc - 2, argv + 2, flags);
  816.     }
  817.     } else if ((c == 'c') && (length > 1) &&
  818.            (strncmp(argv[1], "cget", length) == 0)) {
  819.         if (argc != 3) {
  820.             Tcl_AppendResult(interp, "wrong # args: should be \"",
  821.                  argv[0], " cget option\"", (char *) NULL);
  822.         return TCL_ERROR;
  823.         }
  824.         result = Tk_ConfigureValue(interp, graphPtr->tkwin, configSpecs,
  825.                    (char *) graphPtr, argv[2], 0);
  826.     } else if ((c == 'c') && (length > 1) &&
  827.     (strncmp(argv[1], "crosshairs", length) == 0)) {
  828.     result = Blt_CrosshairsCmd(graphPtr, argc, argv);
  829.     } else if ((c == 'e') && (strncmp(argv[1], "element", length) == 0)) {
  830.     result = Blt_ElementCmd(graphPtr, argc, argv);
  831.     } else if ((c == 't') && (strncmp(argv[1], "tag", length) == 0)) {
  832.     result = Blt_TagCmd(graphPtr, argc, argv);
  833.     } else if ((c == 'x') && (length > 1) &&
  834.     (strncmp(argv[1], "xaxis", length) == 0)) {
  835.     result = Blt_AxisCmd(graphPtr, graphPtr->axisArr[X1_AXIS], argc, argv,
  836.         flags);
  837.     } else if ((c == 'x') && (length > 1) &&
  838.     (strncmp(argv[1], "x2axis", length) == 0)) {
  839.     result = Blt_AxisCmd(graphPtr, graphPtr->axisArr[X2_AXIS], argc, argv,
  840.         flags);
  841.     } else if ((c == 'y') && (length > 1) &&
  842.     (strncmp(argv[1], "yaxis", length) == 0)) {
  843.     result = Blt_AxisCmd(graphPtr, graphPtr->axisArr[Y1_AXIS], argc, argv,
  844.         flags);
  845.     } else if ((c == 'y') && (length > 1) &&
  846.     (strncmp(argv[1], "y2axis", length) == 0)) {
  847.     result = Blt_AxisCmd(graphPtr, graphPtr->axisArr[Y2_AXIS], argc, argv,
  848.         flags);
  849.     } else if ((c == 'l') && (length > 1) &&
  850.     (strncmp(argv[1], "legend", length) == 0)) {
  851.     result = Blt_LegendCmd(graphPtr, argc, argv);
  852.     } else if ((c == 'l') && (length > 1) &&
  853.     (strncmp(argv[1], "locate", length) == 0)) {
  854.     /* Obsolete syntax "locate" */
  855.     result = GraphCoords(graphPtr, argc, argv);
  856.     } else if ((c == 'i') && (length > 1) &&
  857.     (strncmp(argv[1], "invtransform", length) == 0)) {
  858.     result = GraphCoords(graphPtr, argc, argv);
  859.     } else if ((c == 't') && (length > 1) &&
  860.     (strncmp(argv[1], "transform", length) == 0)) {
  861.     result = WindowCoords(graphPtr, argc, argv);
  862.     } else if ((c == 'p') && (strncmp(argv[1], "postscript", length) == 0)) {
  863.     result = (*graphPtr->postscript->printProc) (graphPtr, argc, argv);
  864.     } else if ((c == 'p') && (length > 1) &&
  865.     (strncmp(argv[1], "psconfigure", length) == 0)) {
  866.     result = (*graphPtr->postscript->configProc) (graphPtr, argc, argv);
  867.     } else {
  868.     Tcl_AppendResult(interp, "bad option \"", argv[1], "\": should be\
  869.  configure, crosshairs, element, invtransform, legend, postscript,\
  870.  psconfigure, tag, transform, xaxis, yaxis, or y2axis", (char *)NULL);
  871.     }
  872.     Tk_Release((ClientData)graphPtr);
  873.     return result;
  874. }
  875.  
  876. /*
  877.  * -----------------------------------------------------------------
  878.  *
  879.  * Blt_LayoutGraph --
  880.  *
  881.  *    Based upon the computes sizing of the plotting area, calls
  882.  *    routines to calculate the positions for the graph components.
  883.  *    This routine is called when the layout changes.  It is called
  884.  *    from DisplayGraph and PrintPostScript.
  885.  *
  886.  * Results:
  887.  *    None
  888.  *
  889.  * Side Effects:
  890.  *    Old storage is freed and new memory is allocated for holding
  891.  *    component positions.  These positions will eventually affect
  892.  *    where the components are drawn in the graph window.
  893.  *
  894.  * -----------------------------------------------------------------
  895.  */
  896. void
  897. Blt_LayoutGraph(graphPtr)
  898.     Graph *graphPtr;
  899. {
  900.     Blt_ListEntry *entryPtr;
  901.     Element *elemPtr;
  902.     Tag *tagPtr;
  903.  
  904.     /* Layout axes */
  905.     if (graphPtr->flags & LAYOUT_ALL) {
  906.     GraphAxis *axisPtr;
  907.     register int i;
  908.  
  909.     for (i = 0; i < 4; i++) {
  910.         axisPtr = graphPtr->axisArr[i];
  911.         (*axisPtr->layoutProc) (graphPtr, axisPtr);
  912.     }
  913.     }
  914.     /* Layout elements */
  915.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->elemList));
  916.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  917.     elemPtr = (Element *)Blt_GetListValue(entryPtr);
  918.     if ((graphPtr->flags & LAYOUT_DIRTY) ||
  919.         (elemPtr->flags & LAYOUT_NEEDED)) {
  920.         (*elemPtr->layoutProc) (graphPtr, elemPtr);
  921.         elemPtr->flags &= ~LAYOUT_NEEDED;
  922.     }
  923.     }
  924.  
  925.     /* Layout tags */
  926.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->tagList));
  927.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  928.     tagPtr = (Tag *)Blt_GetListValue(entryPtr);
  929.     if ((graphPtr->flags & LAYOUT_DIRTY) ||
  930.         (tagPtr->flags & LAYOUT_NEEDED)) {
  931.         (*tagPtr->layoutProc) (graphPtr, tagPtr);
  932.         tagPtr->flags &= ~LAYOUT_NEEDED;
  933.     }
  934.     }
  935.     graphPtr->flags &= ~LAYOUT_ALL;
  936. }
  937.  
  938. /*
  939.  * -----------------------------------------------------------------
  940.  *
  941.  * DisplayElements --
  942.  *
  943.  *    Calls the individual element drawing routines for each
  944.  *    element.
  945.  *
  946.  * Results:
  947.  *    None
  948.  *
  949.  * Side Effects:
  950.  *    Elements are drawn into the drawable (pixmap) which will
  951.  *    eventually be displayed in the graph window.
  952.  *
  953.  * -----------------------------------------------------------------
  954.  */
  955. static void
  956. DisplayElements(graphPtr)
  957.     Graph *graphPtr;
  958. {
  959.     Blt_ListEntry *entryPtr;
  960.     register Element *elemPtr;
  961.  
  962.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->elemList));
  963.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  964.     elemPtr = (Element *)Blt_GetListValue(entryPtr);
  965.     (*elemPtr->displayProc) (graphPtr, elemPtr, ELEM_NORMAL);
  966.     }
  967. }
  968.  
  969. /*
  970.  * -----------------------------------------------------------------
  971.  *
  972.  * DisplayActiveElements --
  973.  *
  974.  *    Calls the individual element drawing routines to display
  975.  *    the active colors for each element.
  976.  *
  977.  * Results:
  978.  *    None
  979.  *
  980.  * Side Effects:
  981.  *    Elements are drawn into the drawable (pixmap) which will
  982.  *    eventually be displayed in the graph window.
  983.  *
  984.  * -----------------------------------------------------------------
  985.  */
  986. static void
  987. DisplayActiveElements(graphPtr)
  988.     Graph *graphPtr;
  989. {
  990.     Blt_ListEntry *entryPtr;
  991.     register Element *elemPtr;
  992.  
  993.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->elemList));
  994.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  995.     elemPtr = (Element *)Blt_GetListValue(entryPtr);
  996.     if (elemPtr->flags & ACTIVE) {
  997.         (*elemPtr->displayProc) (graphPtr, elemPtr, ELEM_ACTIVE);
  998.     }
  999.     }
  1000. }
  1001.  
  1002. /*
  1003.  * -----------------------------------------------------------------
  1004.  *
  1005.  * DisplayTags --
  1006.  *
  1007.  *    Calls the individual tag drawing routines for each tag.
  1008.  *    A tag will not be drawn if there is an element associated
  1009.  *    with the tag (elemId is non-NULL) and that element is
  1010.  *    not in the element display list.
  1011.  *
  1012.  * Results:
  1013.  *    None
  1014.  *
  1015.  * Side Effects:
  1016.  *    Tags are drawn into the drawable (pixmap) which will eventually
  1017.  *    be displayed in the graph window.
  1018.  *
  1019.  * -----------------------------------------------------------------
  1020.  */
  1021. static void
  1022. DisplayTags(graphPtr)
  1023.     Graph *graphPtr;
  1024. {
  1025.     Blt_ListEntry *listPtr;
  1026.     Tag *tagPtr;
  1027.     int mapped;
  1028.  
  1029.     for (listPtr = Blt_FirstListEntry(&(graphPtr->tagList)); listPtr != NULL;
  1030.     listPtr = Blt_NextListEntry(listPtr)) {
  1031.     tagPtr = (Tag *)Blt_GetListValue(listPtr);
  1032.     mapped = 1;
  1033.  
  1034.     if (tagPtr->elemId != NULL) {
  1035.         Tcl_HashEntry *entryPtr;
  1036.         Element *elemPtr;
  1037.  
  1038.         entryPtr = Tcl_FindHashEntry(&(graphPtr->elemTable),
  1039.         (char *)tagPtr->elemId);
  1040.         if (entryPtr == NULL) {
  1041.         continue;
  1042.         }
  1043.         elemPtr = (Element *)Tcl_GetHashValue(entryPtr);
  1044.         mapped = elemPtr->mapped;
  1045.     }
  1046.     if (mapped) {
  1047.         (*tagPtr->displayProc) (graphPtr, tagPtr);
  1048.     }
  1049.     }
  1050. }
  1051.  
  1052. /*
  1053.  * -----------------------------------------------------------------
  1054.  *
  1055.  * DisplayExterior --
  1056.  *
  1057.  *     Draws the exterior region of the graph (axes, ticks, titles, etc)
  1058.  *    onto a pixmap. The interior region is defined by the given
  1059.  *    rectangle structure.
  1060.  *
  1061.  *        X coordinate axis
  1062.  *        Y coordinate axis
  1063.  *        legend
  1064.  *        interior border
  1065.  *        exterior border
  1066.  *        titles (X and Y axis, graph)
  1067.  *
  1068.  * Returns:
  1069.  *    None.
  1070.  *
  1071.  * Side Effects:
  1072.  *    Exterior of graph is displayed in its window.
  1073.  *
  1074.  * -----------------------------------------------------------------
  1075.  */
  1076. static void
  1077. DisplayExterior(graphPtr, rectPtr)
  1078.     Graph *graphPtr;
  1079.     XRectangle *rectPtr;    /* Interior plotting region */
  1080. {
  1081.     int x, y;
  1082.     XRectangle rectArr[4];
  1083.     GraphAxis *axisPtr;
  1084.     TextAttributes textAttr;
  1085.     register int i;
  1086.  
  1087.     /*
  1088.      * Draw the four outer rectangles which encompass the plotting
  1089.      * surface. This clears the surrounding area and clips the plot.
  1090.      */
  1091.  
  1092.     rectArr[0].x = rectArr[0].y = rectArr[3].x = rectArr[1].x = 0;
  1093.     rectArr[0].width = rectArr[3].width = graphPtr->width;
  1094.     rectArr[0].height = rectPtr->y;
  1095.     rectArr[3].y = rectPtr->y + rectPtr->height;
  1096.     rectArr[3].height = graphPtr->height - rectArr[3].y;
  1097.     rectArr[2].y = rectArr[1].y = rectPtr->y;
  1098.     rectArr[1].width = rectPtr->x;
  1099.     rectArr[2].height = rectArr[1].height = rectPtr->height;
  1100.     rectArr[2].x = rectPtr->x + rectPtr->width;
  1101.     rectArr[2].width = graphPtr->width - rectArr[2].x;
  1102.     XFillRectangles(graphPtr->display, graphPtr->canvas,
  1103.     graphPtr->marginFillGC, rectArr, 4);
  1104.  
  1105.     /* Interior 3D border */
  1106.  
  1107.     if ((graphPtr->plotRelief != TK_RELIEF_FLAT) &&
  1108.     (graphPtr->plotBW > 0)) {
  1109.     Tk_Draw3DRectangle(graphPtr->tkwin, graphPtr->canvas,
  1110.         graphPtr->border, rectPtr->x, rectPtr->y, rectPtr->width,
  1111.         rectPtr->height, graphPtr->plotBW, graphPtr->plotRelief);
  1112.     }
  1113.     /* Legend */
  1114.     if (graphPtr->legendPtr->useDefault) {
  1115.     (*graphPtr->legendPtr->displayProc) (graphPtr);
  1116.     }
  1117.     /* Initialize text attributes for graph and axis titles */
  1118.  
  1119.     textAttr.theta = 0.0;
  1120.     textAttr.anchor = TK_ANCHOR_CENTER;
  1121.     textAttr.gc = graphPtr->marginGC;
  1122.     textAttr.bgColorPtr = Tk_3DBorderColor(graphPtr->border);
  1123.     textAttr.fontPtr = graphPtr->fontPtr;
  1124.  
  1125.     if (graphPtr->title != NULL) {
  1126.     x = (graphPtr->extreme.x + graphPtr->origin.x) / 2;
  1127.     y = graphPtr->borderWidth + TEXTHEIGHT(graphPtr->fontPtr);
  1128.     Blt_DrawText(graphPtr->display, graphPtr->canvas, graphPtr->title,
  1129.         &textAttr, x, y);
  1130.     }
  1131.     /* Draw axes */
  1132.     for (i = 0; i < 4; i++) {
  1133.     axisPtr = graphPtr->axisArr[i];
  1134.     if (axisPtr->mapped) {
  1135.         (*axisPtr->displayProc) (graphPtr, axisPtr, &textAttr);
  1136.     }
  1137.     }
  1138.     /* Exterior 3D border */
  1139.     if ((graphPtr->relief != TK_RELIEF_FLAT) &&
  1140.     (graphPtr->borderWidth > 0)) {
  1141.     Tk_Draw3DRectangle(graphPtr->tkwin, graphPtr->canvas,
  1142.         graphPtr->border, 0, 0, graphPtr->width, graphPtr->height,
  1143.         graphPtr->borderWidth, graphPtr->relief);
  1144.     }
  1145. }
  1146.  
  1147. /*
  1148.  *----------------------------------------------------------------------
  1149.  *
  1150.  * DisplayGraph --
  1151.  *
  1152.  *    This procedure is invoked to display a graph widget.
  1153.  *
  1154.  * Results:
  1155.  *    None.
  1156.  *
  1157.  * Side effects:
  1158.  *    Commands are output to X to display the graph in its
  1159.  *    current mode.
  1160.  *
  1161.  *----------------------------------------------------------------------
  1162.  */
  1163. static void
  1164. DisplayGraph(clientData)
  1165.     ClientData clientData;
  1166. {
  1167.     register Graph *graphPtr = (Graph *)clientData;
  1168.     XRectangle plotArea;
  1169.     int twiceBW;
  1170.     GraphLegend *legendPtr;
  1171.  
  1172.     /*
  1173.      * If the window has been deleted or is no longer mapped, do
  1174.      * nothing
  1175.      */
  1176.     graphPtr->flags &= ~REDRAW_PENDING;
  1177.     if ((graphPtr->tkwin == NULL) || (!Tk_IsMapped(graphPtr->tkwin))) {
  1178.     return;
  1179.     }
  1180. #ifdef notdef
  1181.     fprintf(stderr, "Calling DisplayGraph\n");
  1182. #endif
  1183.  
  1184.     graphPtr->width = Tk_Width(graphPtr->tkwin);
  1185.     graphPtr->height = Tk_Height(graphPtr->tkwin);
  1186.  
  1187.     if (graphPtr->flags & LAYOUT_NEEDED) {
  1188.     if (graphPtr->flags & LAYOUT_DIRTY) {
  1189.         if (Blt_ComputeLayout(graphPtr) != TCL_OK) {
  1190.         return;        /* Not enough room to plot graph */
  1191.         }
  1192.     }
  1193.     Blt_LayoutGraph(graphPtr);
  1194.     }
  1195.     /*
  1196.      * Determine the plotting surface region of the graph window
  1197.      */
  1198.     twiceBW = (graphPtr->plotBW * 2);
  1199.     plotArea.x = graphPtr->origin.x - graphPtr->plotBW;
  1200.     plotArea.y = graphPtr->extreme.y - graphPtr->plotBW;
  1201.     plotArea.width = (graphPtr->extreme.x - graphPtr->origin.x) + twiceBW;
  1202.     plotArea.height = (graphPtr->origin.y - graphPtr->extreme.y) + twiceBW;
  1203.  
  1204.     /*
  1205.      * Create a pixmap (for double buffering) the size of the window
  1206.      */
  1207.     graphPtr->canvas = XCreatePixmap(graphPtr->display,
  1208.     Tk_WindowId(graphPtr->tkwin), graphPtr->width, graphPtr->height,
  1209.     Tk_Depth(graphPtr->tkwin));
  1210.  
  1211.     if (graphPtr->buffered) {
  1212.     if ((graphPtr->elemWidth != graphPtr->width) ||
  1213.         (graphPtr->elemHeight != graphPtr->height) ||
  1214.         (graphPtr->elemMap == None)) {
  1215.         /*
  1216.          * Create a pixmap to save elements if one doesn't already
  1217.          * exist or the size of the window has changed.
  1218.          */
  1219.         graphPtr->elemWidth = graphPtr->width;
  1220.         graphPtr->elemHeight = graphPtr->height;
  1221.         if (graphPtr->elemMap != None) {
  1222.         XFreePixmap(graphPtr->display, graphPtr->elemMap);
  1223.         }
  1224.         graphPtr->elemMap = XCreatePixmap(graphPtr->display,
  1225.         Tk_WindowId(graphPtr->tkwin), graphPtr->elemWidth,
  1226.         graphPtr->elemHeight, Tk_Depth(graphPtr->tkwin));
  1227.         graphPtr->flags |= DIRTY;
  1228.     }
  1229.     if (graphPtr->flags & DIRTY) {
  1230.         Pixmap save;
  1231.         /*
  1232.          * Clear the background and draw the elements into the pixmap.
  1233.          */
  1234.         XFillRectangles(graphPtr->display, graphPtr->elemMap,
  1235.         graphPtr->plotFillGC, &plotArea, 1);
  1236.         save = graphPtr->canvas;
  1237.         graphPtr->canvas = graphPtr->elemMap;
  1238.         DisplayElements(graphPtr);
  1239.         graphPtr->canvas = save;
  1240.         graphPtr->flags &= ~DIRTY;
  1241.     }
  1242.     XCopyArea(graphPtr->display, graphPtr->elemMap, graphPtr->canvas,
  1243.         graphPtr->marginGC, plotArea.x, plotArea.y,
  1244.         (unsigned int)plotArea.width, (unsigned int)plotArea.height,
  1245.         plotArea.x, plotArea.y);
  1246.     } else {
  1247.     XFillRectangles(graphPtr->display, graphPtr->canvas,
  1248.         graphPtr->plotFillGC, &plotArea, 1);
  1249.     DisplayElements(graphPtr);
  1250.     }
  1251.     DisplayTags(graphPtr);
  1252.  
  1253.     legendPtr = graphPtr->legendPtr;
  1254.     if (!legendPtr->useDefault) {
  1255.     (*legendPtr->displayProc) (graphPtr);
  1256.     }
  1257.     DisplayActiveElements(graphPtr);
  1258.  
  1259.     /* Disable crosshairs and update lengths before drawing to the display */
  1260.     (*graphPtr->crosshairs->toggleProc) (graphPtr);
  1261.     (*graphPtr->crosshairs->updateProc) (graphPtr);
  1262.  
  1263.     if (graphPtr->flags & REFRESH) {
  1264.     DisplayExterior(graphPtr, &plotArea);
  1265.     XCopyArea(graphPtr->display, graphPtr->canvas,
  1266.         Tk_WindowId(graphPtr->tkwin), graphPtr->marginGC, 0, 0,
  1267.         graphPtr->width, graphPtr->height, 0, 0);
  1268.     } else {
  1269.     /* Draw just the interior plotting region */
  1270.  
  1271.     plotArea.x += graphPtr->plotBW;
  1272.     plotArea.y += graphPtr->plotBW;
  1273.     plotArea.width -= (graphPtr->plotBW * 2);
  1274.     plotArea.height -= (graphPtr->plotBW * 2);
  1275.     XCopyArea(graphPtr->display, graphPtr->canvas,
  1276.         Tk_WindowId(graphPtr->tkwin), graphPtr->marginGC, plotArea.x,
  1277.         plotArea.y, (unsigned int)plotArea.width,
  1278.         (unsigned int)plotArea.height, plotArea.x, plotArea.y);
  1279.     }
  1280.     (*graphPtr->crosshairs->toggleProc) (graphPtr);
  1281.  
  1282.     XFreePixmap(graphPtr->display, graphPtr->canvas);
  1283.     graphPtr->canvas = None;
  1284.     graphPtr->flags &= ~REFRESH;
  1285. }
  1286.  
  1287. int
  1288. Blt_GraphInit(interp)
  1289.     Tcl_Interp *interp;
  1290. {
  1291.     Tk_Window tkwin;
  1292.  
  1293.     if (Blt_FindCmd(interp, "blt_graph", (ClientData *)NULL) == TCL_OK) {
  1294.     Tcl_AppendResult(interp, "\"blt_graph\" command already exists",
  1295.         (char *)NULL);
  1296.     return TCL_ERROR;
  1297.     }
  1298.     tkwin = Tk_MainWindow(interp);
  1299.     if (tkwin == NULL) {
  1300.     Tcl_AppendResult(interp, "\"blt_graph\" requires Tk", (char *)NULL);
  1301.     return TCL_ERROR;
  1302.     }
  1303.     Tcl_SetVar2(interp, "blt_versions", "blt_graph", GRAPH_VERSION,
  1304.     TCL_GLOBAL_ONLY);
  1305.     Tcl_CreateCommand(interp, "blt_graph", GraphCmd, (ClientData)tkwin,
  1306.     (Tcl_CmdDeleteProc *)NULL);
  1307.     return TCL_OK;
  1308. }
  1309.  
  1310. int
  1311. Blt_BarchartInit(interp)
  1312.     Tcl_Interp *interp;
  1313. {
  1314.     Tk_Window tkwin;
  1315.  
  1316.     if (Blt_FindCmd(interp, "blt_barchart", (ClientData *)NULL) == TCL_OK) {
  1317.     Tcl_AppendResult(interp, "\"blt_barchart\" command already exists",
  1318.         (char *)NULL);
  1319.     return TCL_ERROR;
  1320.     }
  1321.     tkwin = Tk_MainWindow(interp);
  1322.     if (tkwin == NULL) {
  1323.     Tcl_AppendResult(interp, "\"blt_barchart\" requires Tk",
  1324.         (char *)NULL);
  1325.     return TCL_ERROR;
  1326.     }
  1327.     Tcl_SetVar2(interp, "blt_versions", "blt_barchart", GRAPH_VERSION,
  1328.     TCL_GLOBAL_ONLY);
  1329.     Tcl_CreateCommand(interp, "blt_barchart", GraphCmd, (ClientData)tkwin,
  1330.     (Tcl_CmdDeleteProc *)NULL);
  1331.     return TCL_OK;
  1332. }
  1333.